// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file provides tools for measuring time intervals and reporting them to
// UMA histograms.
// WARNING: *UMA_HISTOGRAM_* macros in this file are not thread-safe.
// See also: "base/metrics/histogram_macros*.h".
//
// TODO(pkalinnikov): Consider moving content of this file to "base/metrics/*"
// after some refactoring. Note that most of the code generated by the macros
// below is not thread-safe.

#ifndef COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_H_
#define COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_H_

#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
#include "components/subresource_filter/core/common/scoped_timers.h"

namespace subresource_filter {

// Creates a scoped object that measures its lifetime using base::ThreadTicks,
// and reports the result in milliseconds as a UMA statistic to a histogram with
// the provided |name| which is expected to be a runtime constant. The histogram
// collects times up to 10 seconds in 50 buckets.
//
// Under the hood there is a static base::HistogramBase* pointer initialized
// right before the scoped object. The pointer is used by a specific
// |export_functor| passed in to a SCOPED_THREAD_TIMER (see it above).
//
// Example:
//   void Function() {
//     SCOPED_UMA_HISTOGRAM_THREAD_TIMER("Component.FunctionTime");
//     ... Useful things happen here ...
//   }
//
// WARNING: The generated code is not thread-safe.
#define SCOPED_UMA_HISTOGRAM_THREAD_TIMER(name)                             \
  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                                 \
      name, impl::ThreadTicksProvider, impl::ExportMillisecondsToHistogram, \
      10 * 1000, __COUNTER__)

// Similar to SCOPED_UMA_HISTOGRAM_THREAD_TIMER above, but the histogram
// collects times in microseconds, up to 1 second, and using 50 buckets.
//
// WARNING: The generated code is not thread-safe.
#define SCOPED_UMA_HISTOGRAM_MICRO_THREAD_TIMER(name)                       \
  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                                 \
      name, impl::ThreadTicksProvider, impl::ExportMicrosecondsToHistogram, \
      1000 * 1000, __COUNTER__)

// Similar to SCOPED_UMA_HISTOGRAM_TIMER in "base/metrics/histogram_macros.h",
// but the histogram stores times in microseconds, up to 1 second, in 50
// buckets.
//
// WARNING: The generated code is not thread-safe.
#define SCOPED_UMA_HISTOGRAM_MICRO_TIMER(name)                            \
  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(                               \
      name, impl::TimeTicksProvider, impl::ExportMicrosecondsToHistogram, \
      1000 * 1000, __COUNTER__)

// Similar to UMA_HISTOGRAM_TIMES in "base/metrics/histogram_macros.h", but
// the histogram stores times in microseconds, up to 1 second, in 50 buckets.
//
// WARNING: The generated code is not thread-safe.
#define UMA_HISTOGRAM_MICRO_TIMES(name, sample)                         \
  UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample, base::Microseconds(1), \
                                   base::Seconds(1), 50)

// This can be used when the default ranges are not sufficient. This macro lets
// the metric developer customize the min and max of the sampled range, as well
// as the number of buckets recorded.
#define UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(name, sample, min, max, bucket_count) \
  IMPL_UMA_HISTOGRAM_ADD(name, sample.InMicroseconds(), min.InMicroseconds(),  \
                         max.InMicroseconds(), bucket_count)

// -----------------------------------------------------------------------------
// Below are helpers used by other macros. Shouldn't be used directly. ---------

// This is necessary to expand __COUNTER__ to an actual value.
#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_EXPANDER(               \
    name, time_provider, histogram_exporter, max_value, suffix) \
  IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(                       \
      name, time_provider, histogram_exporter, max_value, suffix)

// Creates a static histogram pointer and a scoped object referring to it
// throught the |histogram_exporter| functor. Both the pointer and the scoped
// object are uniquely-named, using the unique |suffix| passed in.
#define IMPL_SCOPED_UMA_HISTOGRAM_TIMER_UNIQUE(                            \
    name, time_provider, histogram_exporter, max_value, suffix)            \
  IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, 1, max_value, 50, suffix) \
  auto scoped_uma_histogram_timer_##suffix =                               \
      impl::ScopedTimerImplFactory<time_provider>::Start(                  \
          histogram_exporter(histogram_##suffix));

// This is necessary to expand __COUNTER__ to an actual value.
#define IMPL_UMA_HISTOGRAM_MICRO_TIMES_EXPANDER(name, max_value, suffix, \
                                                sample)                  \
  IMPL_UMA_HISTOGRAM_MICRO_TIMES_UNIQUE(name, max_value, suffix, sample)

// Defines a static UMA histogram pointer and writes a |sample| to it.
#define IMPL_UMA_HISTOGRAM_ADD(name, sample, min, max, bucket_count)          \
  do {                                                                        \
    IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, 0) \
    histogram_0->Add(sample);                                                 \
  } while (0)

// Defines a static pointer to a UMA histogram.
//
// WARNING: Static local variable initialization is deliberately *not*
// thread-safe in Chrome builds. See the "-fno-threadsafe-statics" flag in
// "build/config/compiler/BUILD.gn" and "/Zc:threadSafeInit-" in
// "build/config/win/BUILD.gn" for details.
#define IMPL_DEFINE_STATIC_UMA_HISTOGRAM_POINTER(name, min, max, bucket_count, \
                                                 suffix)                       \
  static base::HistogramBase* histogram_##suffix =                             \
      base::Histogram::FactoryGet(                                             \
          name, min, max, bucket_count,                                        \
          base::HistogramBase::kUmaTargetedHistogramFlag);

namespace impl {

// ExportFunctor implementation that puts measurements into a UMA |histogram|.
template <bool is_microsec_precision>
class ExportTimeDeltaToHistogram {
 public:
  ExportTimeDeltaToHistogram(base::HistogramBase* histogram)
      : histogram_(histogram) {}

  void operator()(base::TimeDelta duration) {
    if (is_microsec_precision)
      histogram_->Add(duration.InMicroseconds());
    else
      histogram_->Add(duration.InMilliseconds());
  }

 private:
  raw_ptr<base::HistogramBase> histogram_;
};

using ExportMillisecondsToHistogram = ExportTimeDeltaToHistogram<false>;
using ExportMicrosecondsToHistogram = ExportTimeDeltaToHistogram<true>;

}  // namespace impl

}  // namespace subresource_filter

#endif  // COMPONENTS_SUBRESOURCE_FILTER_CORE_COMMON_TIME_MEASUREMENTS_H_
